package com.github.xzb617.client.route.zuul.filter;

import com.github.xzb617.client.route.zuul.props.RouteProperties;
import com.github.xzb617.client.route.zuul.rules.RoutingRuleChain;
import com.github.xzb617.client.route.zuul.rules.impl.HeaderRoutingRule;
import com.github.xzb617.client.route.zuul.rules.impl.IpRoutingRule;
import com.github.xzb617.client.route.zuul.rules.impl.QueryParamRoutingRule;
import com.github.xzb617.client.route.zuul.rules.RoutingRule;
import com.github.xzb617.client.route.zuul.transfers.ZuulHeaderTransfer;
import com.github.xzb617.client.route.zuul.utils.WebUtil;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.List;

/**
 * Zuul路由相关的过滤器
 * <P>
 *     动态流量染色：粘性金丝雀、蓝绿部署
 *     路由负载均衡策略：Ribbon
 *     RPC框架支持：
 *          (1) Feign 已支持
 *         （2）Dubbo 待支持
 * </P>
 * @author xzb617
 */
@Component
public class RouteFilter extends ZuulFilter {

    private final RouteProperties routeProperties;

    public RouteFilter(RouteProperties routeProperties) {
        this.routeProperties = routeProperties;
    }

    @Override
    public String filterType() {
        return "route";
    }

    @Override
    public int filterOrder() {
        // 要求过滤器必须大于5，才能获取serviceId
        return 10;
    }

    @Override
    public boolean shouldFilter() {
        Boolean routeEnabled = this.routeProperties.getEnabled();
        return Boolean.TRUE.equals(routeEnabled);
    }

    @Override
    public Object run() throws ZuulException {
        RequestContext zuulCtx = RequestContext.getCurrentContext();
        HttpServletRequest request = zuulCtx.getRequest();

        // 处理头参透传
        ZuulHeaderTransfer.matchNeedTransferHeadersAndAddToZuulHeader(request);

        // 自定义规则式路由
        String ipAddr = WebUtil.getClientIpAddr(request);
        RoutingRuleChain routingRuleChain = this.buildChain();
        routingRuleChain.executeMatches(request, ipAddr, this.routeProperties.getRule(), routingRuleChain);

        return null;
    }

    /**
     * 构建规则链
     */
    private RoutingRuleChain buildChain() {
        List<RoutingRule> routingRules = new ArrayList<>(3);
        routingRules.add(new IpRoutingRule());
        routingRules.add(new HeaderRoutingRule());
        routingRules.add(new QueryParamRoutingRule());
        return new RoutingRuleChain(routingRules);
    }
}
